
#ifndef __rttl_play_c__
#define __rttl_play_c__

#include "rttl_play.h"
#include "rttl_timer.h"

#include "led.h"

void play_note(short duration, char note, short sharp, short octave, short dotted) {
	int freq = 0;
	// set frequency based on 7th octave
	switch (note) {
		case 'A':
		case 'a':
			// A  3520.00
			// A# 3729.31
			freq = sharp ? 3729 : 3520;
			break;

		case 'B':
		case 'b':
			// B  3951.07
			freq = 3951;
			break;

		case 'C':
		case 'c':
			// C  2049.32
			// C# 2217.46
			freq = sharp ? 2217 : 2093;
			break;

		case 'D':
		case 'd':
			// D  2349.32
			// D# 2489.02
			freq = sharp ? 2489 : 2349;
			break;

		case 'E':
		case 'e':
			// E  2637.02
			freq = 2637;
			break;

		case 'F':
		case 'f':
			// F  2793.83
			// F# 2959.96
			freq = sharp ? 2960 : 2794;
			break;

		case 'G':
		case 'g':
			// G  3135.96
			// G# 3322.44
			freq = sharp ? 3322 : 3136;
			break;

		case 'P':
		case 'p':
			freq = 0;
			break;
	}

	// shift to desired octave
	switch(octave) {
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
			freq = freq >> (7-octave);
			break;
		case 7:
			// do nothing
			break;
		case 8:
		case 9:
		case 10:
			freq = freq << (octave-7);
			break;

		default:
			freq = 0;
			break;
	}

	// set output frequency
	set_timer1(freq);

	// avoid div by zero
	if (duration == 0) return;

	delay_beats((128 / duration) + dotted * (64 / duration));
}


void set_tempo(short tempo_bpm) {
	int t = (32*tempo_bpm);
	set_timer0(t/60);
}

#define HEADER        0x00
#define DEFAULT_TYPE  0x02
#define DEFAULT_VALUE 0x03
#define NOTE_DURATION 0x04
#define NOTE_OCTAVE   0x05
#define ERROR         0xF0

void play_string(const char* str) {

	short defduration = 4;
	short defoctave   = 5;
	short tempo       = 92;

	short i = 0;
	char  c;
	short mode = HEADER;

	short duration = 0;
	char  note     ='p';
	short sharp    = 0;
	short octave   = 0;
	short dotted   = 0;

	while ((c = str[i++])>0) {
		switch(mode) {
			case HEADER:
				if (c == ':') {
					mode = DEFAULT_TYPE;
				}
				break;

			case DEFAULT_TYPE:
				if (c == '=') {
					mode = DEFAULT_VALUE;
				} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
					note = c;
				} else if (c == ' ') {
					//do nothing
				} else {
					mode = ERROR;
				}
				break;

			case DEFAULT_VALUE:
				if (c == ',') {
					if (note == 'd' || note == 'D') {
						defduration = duration;
						note = 'p';
					} else if (note == 'o' || note == 'O') {
						defoctave = duration;
						note = 'p';
					} else if (note == 'b' || note == 'B') {
						tempo = duration;
						note = 'p';
						set_tempo(tempo);
					} else {
						mode = ERROR;
					}
					duration = 0;
					note     ='p';
					mode = DEFAULT_TYPE;
				} else if (c == ':') {
					if (note == 'd' || note == 'D') {
						defduration = duration;
						note = 'p';
					} else if (note == 'o' || note == 'O') {
						defoctave = duration;
						note = 'p';
					} else if (note == 'b' || note == 'B') {
						tempo = duration;
						note = 'p';
						set_tempo(tempo);
					} else {
						mode = ERROR;
					}

					duration = 0;
					note     ='p';
					sharp    = 0;
					octave   = 0;
					dotted   = 0;
					mode = NOTE_DURATION;
				} else if ((c >= '0' && c <= '9')) {
					duration = (duration*10)+(c-'0');
				} else if (c == ' ') {
					//do nothing
				} else {
					mode = ERROR;
				}
				break;

			case NOTE_DURATION:
				if (c == ',') {
					duration = 0;
					note     ='p';
					sharp    = 0;
					octave   = 0;
					dotted   = 0;
					mode = NOTE_DURATION;
				} else if ((c >= '0' && c <= '9')) {
					duration = (duration*10)+(c-'0');
				} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
					note = c;
					mode = NOTE_OCTAVE;
				} else if (c == ' ') {
					//do nothing
				} else {
					mode = ERROR;
				}
				break;

			case NOTE_OCTAVE:
				if (c == ',') {
					if (duration==0) duration = defduration;
					if (octave==0) octave = defoctave;

					play_note(duration, note, sharp, octave, dotted);

					duration = 0;
					note     ='p';
					sharp    = 0;
					octave   = 0;
					dotted   = 0;
					mode = NOTE_DURATION;
				} else if ((c >= '0' && c <= '9')) {
					octave = (octave*10)+(c-'0');
				} else if (c == '#') {
					sharp = 1;
				} else if (c == '.') {
					dotted = 1;
				} else if (c == ' ') {
					//do nothing
				} else {
					mode = ERROR;
				}
				break;
		}

		if (mode == ERROR) {
			mode = HEADER;
			return;
		}
	}
	if (mode == NOTE_OCTAVE) {
		if (duration==0) duration = defduration;
		if (octave==0) octave = defoctave;
		play_note(duration, note, sharp, octave, dotted);
	}

	play_note(1, 'p', 0, 0, 0);

	mode = HEADER;
}

void play_scale(void) {
	short i;

	for(i = 1; i<10; ++i){
		SetLEDs(i);

		play_note(8, 'c', 0, i, 0);
		play_note(8, 'c', 1, i, 0);
		play_note(8, 'd', 0, i, 0);
		play_note(8, 'd', 1, i, 0);
		play_note(8, 'e', 0, i, 0);
		play_note(8, 'f', 0, i, 0);
		play_note(8, 'f', 1, i, 0);
		play_note(8, 'g', 0, i, 0);
		play_note(8, 'g', 1, i, 0);
		play_note(8, 'a', 0, i, 0);
		play_note(8, 'a', 1, i, 0);
		play_note(8, 'b', 0, i, 0);
	}

	play_note(1, 'p', 0, 0, 0);
}

#endif